package com.synergy.stockflow.controller;

import com.synergy.stockflow.annotation.Log;
import com.synergy.stockflow.annotation.LogModule;
import com.synergy.stockflow.constant.HttpStatus;
import com.synergy.stockflow.constant.ResultCode;
import com.synergy.stockflow.exception.BusinessException;
import com.synergy.stockflow.model.Result;
import com.synergy.stockflow.service.JwtService;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/auth")
@Log4j2
@LogModule(name = "权限认证")
public class AuthController {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtService jwtService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    /**
     * 用户登录接口
     */
    @PostMapping("/login")
    @Log(title = "用户登陆", description = "用户根据用户名和密码进行登陆", logType = "LOGIN")
    public Result<Map<String, String>> login(@RequestBody LoginRequest request) {
        try {
            // 1. 执行认证
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
            );
            log.debug("Username: " + authentication.getName());
            log.debug("Authorities: " + authentication.getAuthorities()); // ✅ 应该有权限 CODE
            log.debug("Principal: " + authentication.getPrincipal());
            // 2. 设置认证上下文
            SecurityContextHolder.getContext().setAuthentication(authentication);

            // 3. 获取用户名
            String username = authentication.getName();

            // 4. 检查该用户是否已有有效 token（实现登出旧会话）
            String userTokenKey = "user_token:" + username;
            String oldToken = stringRedisTemplate.opsForValue().get(userTokenKey);

            if (oldToken != null && !oldToken.trim().isEmpty()) {
                // 如果旧 token 存在且未过期，加入黑名单
                if (!jwtService.isExpired(oldToken)) {
                    long expiration = jwtService.getExpiration(oldToken);
                    long remainingTime = expiration - System.currentTimeMillis();
                    if (remainingTime > 0) {
                        stringRedisTemplate.opsForValue().set(
                                "blacklist:" + oldToken,
                                "true",
                                remainingTime,
                                TimeUnit.MILLISECONDS
                        );
                    }

                }
                // 删除旧 token 记录
                stringRedisTemplate.delete(userTokenKey);
            }

            // 5. 生成新 token
            String token = request.isRememberMe()
                    ? jwtService.generateRememberMeToken(username)
                    : jwtService.generateToken(username);

            //6. 将新 token 存入 Redis，用于后续登出或刷新
            long ttl = request.isRememberMe() ?
                    jwtService.getJwtProperties().getRemember() :
                    jwtService.getJwtProperties().getExpiration();

            stringRedisTemplate.opsForValue().set(
                    userTokenKey,
                    token,
                    ttl,
                    TimeUnit.MILLISECONDS
            );

            // 7. 构造返回结果
            Map<String, String> tokenMap = new HashMap<>();
            tokenMap.put("token", jwtService.withPrefix(token));
            tokenMap.put("username", username);

            return Result.success(tokenMap, "登录成功！");
        } catch (BadCredentialsException e) {
            HashMap<String, String> hashMap = new HashMap<>();
            return Result.error(HttpStatus.UNAUTHORIZED, "用户名或密码错误", hashMap);
        } catch (Exception e) {
            HashMap<String, String> hashMap = new HashMap<>();
            log.warn("无法将旧 token 加入黑名单", e);
            return Result.error(HttpStatus.BAD_REQUEST, "登陆异常", hashMap);
        }
    }

    @PostMapping("/logout")
    @Log(title = "用户退出登陆", logType = "LOGIN", logParams = false)
    @CacheEvict(cacheNames = {"auth", "permission"}, allEntries = true)
    public Result<Map<String, String>> logout(HttpServletRequest request) {
        String token = jwtService.extractToken(request);

        if (token != null && !jwtService.isExpired(token)) {
            try {
                String username = jwtService.extractSubject(token);
                long expiration = jwtService.getExpiration(token);
                long remainingTime = expiration - System.currentTimeMillis();

                if (remainingTime > 0) {
                    // 加入黑名单
                    stringRedisTemplate.opsForValue().set(
                            "blacklist:" + token,
                            "true",
                            remainingTime,
                            TimeUnit.MILLISECONDS
                    );
                }

                // 删除用户当前 token 记录
                stringRedisTemplate.delete("user_token:" + username);

            } catch (Exception e) {
                throw new BusinessException(ResultCode.BUSINESS_EXCEPTION, "token 处理失败");
            }
        }

        SecurityContextHolder.clearContext();

        Map<String, String> data = Collections.singletonMap("message", "已成功登出");
        return Result.success(data);
    }

    // 请求体类
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class LoginRequest {
        @Schema(description = "", example = "admin")
        private String username;
        @Schema(description = "", example = "123456")
        private String password;
        private boolean rememberMe;
    }
}